/**
 * 判断是否为函数
 * @method isObject
 * @param {any}} value 需要检查的值
*/
export const isFunction = function (value) {
    return Object.prototype.toString.call(value) === '[object Function]'
}

/**
 * 判断是否为对象
 * @method isObject
 * @param {any}} value 需要检查的值
*/
export const isObject = function (value) {
    return Object.prototype.toString.call(value) === '[object Object]'
}

/**
 * 是否为JSON字符串
 * @method isJSON
 * @param {any}} value 需要检查的值
*/
export const isJSON = function (str) {
    if (typeof str == 'string') {
        try {
            var obj = JSON.parse(str);
            if (typeof obj == 'object' && obj) {
                return true;
            } else {
                return false;
            }
        } catch (e) {
            return false;
        }
    }
}

/**
 * 判断是否为字符串
 * @method isString
 * @param {any}} value 需要检查的值
*/
export const isString = function (value) {
    return Object.prototype.toString.call(value) === '[object String]'
}

/**
 * 判断是否为中文字符串
 * @method isChineseString
 * @param {any}} value 需要检查的值
*/
export const isChineseString = function (c) {
    const reg = /^[\u4E00-\u9FA5\uf900-\ufa2d]/;
    return reg.test(c);
}

/**
 * 英文字符串
 * @method isEnglishString
 * @param {any}} value 需要检查的值
*/
export const isEnglishString = function (c) {
    const reg = /^[a-zA-Z]/;
    return reg.test(c);
}

/**
 * 判断是否为数组
 * @method isArray
 * @param {any}} value 需要检查的值
*/
export const isArray = function (value) {
    return Array.isArray ? Array.isArray(value) : Object.prototype.toString.call(value) === '[object Array]'
}

/**
 * 判断是否为数字， str会转为number判断
 * @method isNumber
 * @param {any}} value 需要检查的值
*/
export const isNumber = function (val) {
    var regPos = /^\d+(\.\d+)?$/ // 非负浮点数
    var regNeg = /^(-(([0-9]+\.[0-9]*[1-9][0-9]*)|([0-9]*[1-9][0-9]*\.[0-9]+)|([0-9]*[1-9][0-9]*)))$/ // 负浮点数
    if (regPos.test(val) || regNeg.test(val)) {
        return true
    } else {
        return false
    }
}

/**
 * 判断对象是否为空对象
 * @method isEmptyObject
 * @param {any}} value 需要检查的值
*/
export const isEmptyObject = function (obj) {
    for (var i in obj) {
        return false;
    }
    return true;
}

//
/**
 * 判断在此数组中是否存在指定的值
 * @method inArray
 * @param {array} arr  要检查的数组
 * @param {any} val  要查找的值
*/
export const inArray = function (arr, val) {
    for (var i = 0; i < arr.length; i++) {
        if (arr[i] === val) return true;
    }
    return false;
}

/**
 * 数组去重
 * @method removeRepeat
 * @param {array} arr   要去重的数组
*/
export const removeRepeat = (arr) => [...new Set(arr)]

/**
 * 数组排序
 * @param  {array} source 待排序数组
 * @param  {array} orders 排序字段数组
 * @param  {string}}type   倒序-1 升序-2
 * 调用：var res = orderBy(arr, ['OpenTime'], 1);
 */
export const orderBy = function (source, orders, type) {

    if (source instanceof Array && orders instanceof Array && orders.length > 0) {

        var ordersc = orders.concat([]);
        var sorttype = type || 0;
        var results = [];
        // var totalSum = {};

        const grouporder = (source, orders) => {

            source.sort(function (a, b) {
                var convertA = a[orders[0]];
                var convertB = b[orders[0]];
                if (typeof convertA == 'string' && typeof convertB == 'string') {
                    if (sorttype == 2) {
                        return convertA.localeCompare(convertB);
                    } else {
                        return convertB.localeCompare(convertA);
                    }
                } else {
                    if (sorttype == 2) {
                        return convertA - convertB;
                    } else {
                        return convertB - convertA;
                    }
                }
            });

            var groupmap = new Map();
            source.forEach((item) => {
                if (groupmap.has(item[orders[0]])) {
                    groupmap.get(item[orders[0]]).push(item);
                } else {
                    groupmap.set(item[orders[0]], []);
                    groupmap.get(item[orders[0]]).push(item);
                }
            });

            orders.shift();

            // eslint-disable-next-line no-unused-vars
            for (let [key, val] of groupmap) {

                if (orders.length == 0) {
                    results = results.concat(val);
                } else {
                    var orderscopy = orders.concat([]);
                    grouporder(val, orderscopy);
                }
            }
        }

        grouporder(source, ordersc);

        return results;
    } else {
        return source;
    }
}

/**
 * 节流器：时间戳版本---延迟执行版本
 * @method throttleDelay
 * @param {function} func 传入的方法
 * @param {number} dalay 延迟时间--毫秒
 * @desc 用于在一段时间内只允许函数执行一次，如：表单按钮提交
*/
export const throttleDelay = function (func, delay) {
    let timer = null
    return function () {
        let context = this
        let args = arguments
        if (!timer) {
            timer = setTimeout(function () {
                func.apply(context, args)
                timer = null
            }, delay)
        }
    }
}

/**
 * 节流器：时间戳版本---立即执行版本
 * @method throttleImmediately
 * @param {function} func 传入的方法
 * @param {number} dalay  延迟时间--毫秒
 * @desc 用于在一段时间内只允许函数执行一次，如：表单按钮提交
*/
export const throttleImmediately = function (func, delay) {
    let timer = null
    return function () {
        let context = this
        let args = arguments
        if (!timer) {
            func.apply(context, args)
            timer = setTimeout(function () {
                timer = null
            }, delay)
        }
    }
}

/**
 * 流程控制：延迟函数
 * @method sleep
 * @param {number}  time  延迟时间--毫秒
*/
export const sleep = time => new Promise(resolve => setTimeout(resolve, time))

/**
 * 流程控制：检查fn函数，直到 fn 返回 true
 * @method until
 * @param {function} fn  要检查的函数
 * @param {number} inteval  间隔时间
 * @desc 不停的检查fn函数，直到 fn 返回 true ,才返回（resolve）
*/
export const until = function (fn, inteval = 50) {
    return new Promise(resolve => {
        if (fn()) {
            resolve()
        } else {
            const timer = setInterval(() => {
                if (fn()) {
                    clearInterval(timer)
                    resolve()
                }
            }, inteval)
        }
    })
}

/**
 * 防抖函数
 * @method debounce
 * @param {function}  func  要执行的函数
 * @param {number}    delay   延迟时间
 * @param {boolen}    immediate   是否立即执行, 默认true
 * @desc 用于在短时间内多次触发同一个函数，只执行最后一次，或者只在开始时执行，如：拖拽改变窗口大小
*/
export const debounce = function (func, delay, immediate = true) {
    let timer = null
    return function () {
        let context = this
        let args = arguments
        if (timer) clearTimeout(timer)
        if (immediate) {
            var doNow = !timer
            timer = setTimeout(function () {
                timer = null
            }, delay)
            if (doNow) func.apply(context, args)
        } else {
            timer = setTimeout(function () {
                func.apply(context, args)
            }, delay)
        }
    }
}

/**
 * 解析url参数
 * @method urlToObj
 * @param {string} url  传入的url，如果不传则取window.location.href
 * @desc "alipay_sdk=alipay-sdk-java-4.8.10.ALL&app_id=2019070165756378&version=1.0"
*/
export const urlToObj = function (url) {
    let _url = url || window.location.href || ''
    let search = _url.match(/\?(\S*)(#|\S)/)
    let _arr = search ? search[0].replace(/\?/, '').split('&') : []
    let res = {}
    for (let i = 0; i < _arr.length; i++) {
        let str = _arr[i].split('=')
        if (str[0] !== '') {
            res[str[0]] = str[1]
        }
    }
    return res
}

/**
 * 对象转为url参数
 * @method objToUrl
 * @param {object} obj 需要转为url参数的对象
*/
export const objToUrl = function (obj, { assign = '=', joint = '&' } = {}) {
    const params = []
    Object.keys(obj).forEach((key) => {
        let value = obj[key]
        // 如果值为undefined我们将其置空
        if (typeof value === 'undefined') {
            value = ''
        }
        // params.push([key, encodeURIComponent(value)].join('='))// 对于需要编码的文本（比如说中文）我们要进行编码
        params.push([key, value].join(assign))
    })
    return params.join(joint)
}

/**
 * 对象转base64
 * @method encodeToBase64
*/
export const encodeToBase64 = function (val) {
    let encodeString = ''
    if (isObject(val)) {
        encodeString = JSON.stringify(val)
    } else {
        encodeString = val
    }
    const encode = encodeURI(encodeString)
    return btoa(encode)
}

/**
 * 像素转vh单位
 * @method pxToVh
*/
export const pxToVh = function (px) {
    return (px / window.devicePixelRatio) / (window.screen.height / 100)
}


/**
 * 移动端H5下载图片
 * @method savePictureByH5
 * @param {string} url   图片地址
*/
export const savePictureByH5 = function (Url) {
    var blob = new Blob([''], { type: 'application/octet-stream' })
    var url = URL.createObjectURL(blob)
    var a = document.createElement('a')

    a.href = Url
    a.download = 'img'
    var e = document.createEvent('MouseEvents')
    e.initMouseEvent('click', true, false, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null)
    a.dispatchEvent(e)
    URL.revokeObjectURL(url)
}

/**
 * 阿里云图片
 * @method ossProcessImg
 * @param {string} imgSrc  图片链接
 * @param {string} type  图片缩放类型
 * @param {object}} { width , height }  图片宽高
*/
export const ossProcessImg = function (imgSrc, type, { width = 0, height = 0 } = {}) {
    let process = '?x-oss-process=image'
    switch (type) {
        case 'orient':
            process += '/resize,h_4096,w_4096/auto-orient,1'
            break
        case 'resizeFill':
            process += '/resize,m_fill,w_' + width + ',h_' + height
            break
        case 'lfit':  // 等比缩放
            process += `/resize,m_lfit,${width ? 'w_' + width : ''},${height ? 'h_' + height : ''}`
            break;
        default:
            break
    }
    return imgSrc + process
}

/**
 * 将base64转换为file
 * @method dataURLtoFile
 * @param {string} dataurl  base64数据
 * @param {string} filename  文件名
*/
export const dataURLtoFile = function (dataurl, filename) { //将base64转换为文件
    var arr = dataurl.split(','),
        mime = arr[0].match(/:(.*?);/)[1],
        bstr = atob(arr[1]),
        n = bstr.length,
        u8arr = new Uint8Array(n),
        blob;
    while (n--) {
        u8arr[n] = bstr.charCodeAt(n);
    }

    // ios10及以下不支持 new File()
    blob = new Blob([u8arr], {
        type: mime
    });

    blob.lastModifiedDate = new Date();
    blob.name = filename;
    return blob;
}

/**
 * 生成随机字符串
 * @method randomString
 * @param {number} len  生成的长度
*/
export const randomString = function (len = 48) {
    const chars = 'ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz2345678' // 默认去掉了容易混淆的字符oOLl,9gq,Vv,Uu,I1
    const maxPos = chars.length
    let str = ''
    let i = 0
    while (i < len) {
        str += chars.charAt(Math.floor(Math.random() * maxPos))
        if (i == 0 && [2, 3, 4, 5, 6, 7, 8].includes(str)) {
            // 首个字符不能是数字
        } else {
            i++
        }
    }
    return str
}

/**
 * 因为跨域问题 图片要转Base64
 * @method imgToBase64CORS
 * @param {string} url  图片链接
 * @param {boolean} isCompress  是否压缩
*/
export const imgToBase64CORS = function (url, isCompress) {
    if (!url) return
    return new Promise((resolve) => {
        let canvas = document.createElement('canvas')
        let ctx = canvas.getContext('2d')
        let img = new Image()
        var ext = url.substring(url.lastIndexOf('.') + 1).toLowerCase()
        canvas.setAttribute('id', 'canvasSS')
        img.crossOrigin = 'Anonymous'
        img.onload = () => {
            if (!isCompress) {
                // 不压缩
                canvas.width = img.width
                canvas.height = img.height
                canvas.style.width = img.width
                canvas.style.height = img.height
                ctx.drawImage(img, 0, 0, img.width, img.height)
                let dataURL = canvas.toDataURL('image/' + ext, 0.4)
                canvas = null
                resolve(dataURL)
            } else {
                // 使用瓦片canvas压缩
                let canvas = document.createElement('canvas')
                let ctx = canvas.getContext('2d')
                // 瓦片canvas
                let tCanvas = document.createElement('canvas')
                let tctx = tCanvas.getContext('2d')
                // let initSize = img.src.length;
                let width = img.width
                let height = img.height
                // 如果图片大于四百万像素，计算压缩比并将大小压至400万以下
                let ratio
                if ((ratio = (width * height) / 4000000) > 1) {
                    // console.log("大于400万像素");
                    ratio = Math.sqrt(ratio)
                    width /= ratio
                    height /= ratio
                } else {
                    ratio = 1
                }
                canvas.width = width
                canvas.height = height
                //        铺底色
                ctx.fillStyle = '#fff'
                ctx.fillRect(0, 0, canvas.width, canvas.height)
                // 如果图片像素大于100万则使用瓦片绘制
                let count
                if ((count = (width * height) / 1000000) > 1) {
                    // console.log("超过100W像素");
                    count = ~~(Math.sqrt(count) + 1) // 计算要分成多少块瓦片
                    //            计算每块瓦片的宽和高
                    let nw = ~~(width / count)
                    let nh = ~~(height / count)
                    tCanvas.width = nw
                    tCanvas.height = nh
                    for (let i = 0; i < count; i++) {
                        for (let j = 0; j < count; j++) {
                            tctx.drawImage(
                                img,
                                i * nw * ratio,
                                j * nh * ratio,
                                nw * ratio,
                                nh * ratio,
                                0,
                                0,
                                nw,
                                nh
                            )
                            ctx.drawImage(tCanvas, i * nw, j * nh, nw, nh)
                        }
                    }
                } else {
                    ctx.drawImage(img, 0, 0, width, height)
                }
                // 进行最小压缩
                let ndata = canvas.toDataURL('image/' + ext, 0.6)
                tCanvas.width = tCanvas.height = canvas.width = canvas.height = 0
                resolve(ndata)
            }
        }
        img.onerror = function () {
            console.log("读取图片错误")
        }
        img.src = url + '?v=' + Math.random()
    })
}

/**
 * 去除两端空格
 * @method trim
 * @param {string} value 
*/
export const trim = function (value) {
    return (value || '').replace(/^[\s\uFEFF]+|[\s\uFEFF]+$/g, '');
}

/**
 * 深度克隆
 * @method deepClone
 * @param {object} url  要克隆的对象
*/
export const deepClone = function (obj) {
    // 先检测是不是数组和Object
    if (isArray(obj)) {
        // 克隆数组
        let newObj = [];
        for (let i = 0; i < obj.length; i++) {
            newObj[i] = this.deepClone(obj[i]);
        }
        return newObj;
    } else if (isJSON(obj)) {
        // 克隆Object
        let newObj = {};
        for (let i in obj) {
            newObj[i] = this.deepClone(obj[i]);
        }
        return newObj;
    }
    // 不是引用类型直接返回
    return obj;
}

/**
 * 保留小数点后N位
 * @method toDecimal
 * @param {number} val  浮点数
 * @param {number} num  保留位数
*/
export const toDecimal = function (val, num) {
    num = num === undefined ? 2 : num;
    val = isNumber(val) ? val : 0
    // 四舍五入
    var vals = Math.round(val * Math.pow(10, num)) / Math.pow(10, num),
        toVal = vals.toString(), len = toVal.indexOf('.');
    // 如果是整数，小数点位置为-1
    if (len < 0) {
        len = toVal.length;
        toVal += '.';
    }
    // 不足位数以零填充
    while (toVal.length <= len + num) {
        toVal += '0';
    }
    return toVal;
}

/**
 * 保留小数点:不四舍五入
 * @method retain
 * @param {number} val  浮点数
 * @param {number} decimal 
*/
export const retain = function (num, decimal) {
    num = num.toString();
    let index = num.indexOf('.');
    if (index !== -1) {
        num = num.substring(0, decimal + index + 1)
    } else {
        num = num.substring(0)
    }
    return parseFloat(num).toFixed(decimal)
}

export default {
    // 判断
    isFunction,
    isObject,
    isJSON,
    isString,
    isChineseString,  // 是否中文字符
    isEnglishString,  // 是否英文字符
    isArray,
    isNumber,
    isEmptyObject,
    //数据处理
    inArray,
    orderBy,        //数组自定义排序
    // 流程控制
    throttleDelay,        // 节流器：时间戳版本---延迟执行版本
    throttleImmediately,  // 节流器：时间戳版本---立即执行版本
    sleep,                // 延迟函数
    until,                // 流程控制：直到条件满足才向下执行
    debounce,             // 流程控制：防抖函数
    // 解析转换
    urlToObj,        // 解析url参数，得到一个对象
    objToUrl,        // 对象转url参数
    encodeToBase64,  // 对象转base64
    pxToVh,          // px转vh单位
    removeRepeat,    // 数组去重
    // 图片文件相关
    savePictureByH5,  // 移动端H5下载图片
    ossProcessImg,    // OSS阿里云图片
    dataURLtoFile,    // 将base64转换为file
    imgToBase64CORS,  // 图片转Base64
    // 其它
    randomString, // 生成随机字符串, 不能数字开头
    deepClone,    // 深度克隆
    toDecimal,    // 保留小数点后N位
    retain,       // 保留小数点:不四舍五入
    trim          // 去除两端空格
}
